package com.seafood.service.impl;

import com.seafood.dto.ColdChainAlertDTO;
import com.seafood.entity.ColdChainAlert;
import com.seafood.entity.ColdChainRecord;
import com.seafood.repository.ColdChainAlertRepository;
import com.seafood.repository.ColdChainRecordRepository;
import com.seafood.service.ColdChainAlertService;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 冷链报警服务实现
 */
@Service
@RequiredArgsConstructor
@Transactional
public class ColdChainAlertServiceImpl implements ColdChainAlertService {
    
    private final ColdChainAlertRepository coldChainAlertRepository;
    private final ColdChainRecordRepository coldChainRecordRepository;
    
    private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    
    @Override
    public ColdChainAlertDTO createColdChainAlert(ColdChainAlertDTO alertDTO) {
        ColdChainAlert alert = new ColdChainAlert();
        copyDtoToEntity(alertDTO, alert);
        
        ColdChainAlert saved = coldChainAlertRepository.save(alert);
        return convertToDTO(saved);
    }
    
    @Override
    public List<ColdChainAlertDTO> createColdChainAlerts(List<ColdChainAlertDTO> alertDTOs) {
        return alertDTOs.stream()
                .map(this::createColdChainAlert)
                .collect(Collectors.toList());
    }
    
    @Override
    @Transactional(readOnly = true)
    public ColdChainAlertDTO getColdChainAlertById(Long id) {
        ColdChainAlert alert = coldChainAlertRepository.findById(id)
                .orElseThrow(() -> new RuntimeException("冷链报警不存在"));
        return convertToDTO(alert);
    }
    
    @Override
    @Transactional(readOnly = true)
    public List<ColdChainAlertDTO> getColdChainAlertsByColdChainRecordId(Long coldChainRecordId) {
        List<ColdChainAlert> alerts = coldChainAlertRepository.findByColdChainRecordIdOrderByAlertTimeDesc(coldChainRecordId);
        return alerts.stream().map(this::convertToDTO).collect(Collectors.toList());
    }
    
    @Override
    @Transactional(readOnly = true)
    public List<ColdChainAlertDTO> getUnresolvedAlerts(Long coldChainRecordId) {
        ColdChainRecord record = coldChainRecordRepository.findById(coldChainRecordId)
                .orElseThrow(() -> new RuntimeException("冷链记录不存在"));
        
        List<ColdChainAlert> alerts = coldChainAlertRepository.findByColdChainRecordAndIsResolvedFalse(record);
        return alerts.stream().map(this::convertToDTO).collect(Collectors.toList());
    }
    
    @Override
    @Transactional(readOnly = true)
    public Page<ColdChainAlertDTO> getAllUnresolvedAlerts(Pageable pageable) {
        Page<ColdChainAlert> alerts = coldChainAlertRepository.findAllUnresolvedAlerts(pageable);
        return alerts.map(this::convertToDTO);
    }
    
    @Override
    @Transactional(readOnly = true)
    public List<ColdChainAlertDTO> getAlertsByType(String alertType) {
        ColdChainAlert.AlertType type = ColdChainAlert.AlertType.valueOf(alertType.toUpperCase());
        List<ColdChainAlert> alerts = coldChainAlertRepository.findByAlertType(type);
        return alerts.stream().map(this::convertToDTO).collect(Collectors.toList());
    }
    
    @Override
    @Transactional(readOnly = true)
    public List<ColdChainAlertDTO> getAlertsBySeverity(String severity) {
        ColdChainAlert.Severity sev = ColdChainAlert.Severity.valueOf(severity.toUpperCase());
        List<ColdChainAlert> alerts = coldChainAlertRepository.findBySeverity(sev);
        return alerts.stream().map(this::convertToDTO).collect(Collectors.toList());
    }
    
    @Override
    @Transactional(readOnly = true)
    public List<ColdChainAlertDTO> getHighSeverityUnresolvedAlerts() {
        List<ColdChainAlert> alerts = coldChainAlertRepository.findHighSeverityUnresolvedAlerts();
        return alerts.stream().map(this::convertToDTO).collect(Collectors.toList());
    }
    
    @Override
    @Transactional(readOnly = true)
    public List<ColdChainAlertDTO> getAlertsByTimeRange(LocalDateTime startTime, LocalDateTime endTime) {
        List<ColdChainAlert> alerts = coldChainAlertRepository.findByTimeRange(startTime, endTime);
        return alerts.stream().map(this::convertToDTO).collect(Collectors.toList());
    }
    
    @Override
    @Transactional(readOnly = true)
    public List<ColdChainAlertDTO> getUnsentNotificationAlerts() {
        List<ColdChainAlert> alerts = coldChainAlertRepository.findByNotificationSentFalse();
        return alerts.stream().map(this::convertToDTO).collect(Collectors.toList());
    }
    
    @Override
    public ColdChainAlertDTO resolveAlert(Long id, String resolvedBy, String resolutionNotes) {
        ColdChainAlert alert = coldChainAlertRepository.findById(id)
                .orElseThrow(() -> new RuntimeException("冷链报警不存在"));
        
        alert.setIsResolved(true);
        alert.setResolvedTime(LocalDateTime.now());
        alert.setResolvedBy(resolvedBy);
        alert.setResolutionNotes(resolutionNotes);
        
        ColdChainAlert resolved = coldChainAlertRepository.save(alert);
        return convertToDTO(resolved);
    }
    
    @Override
    public List<ColdChainAlertDTO> resolveAlerts(List<Long> alertIds, String resolvedBy, String resolutionNotes) {
        return alertIds.stream()
                .map(id -> resolveAlert(id, resolvedBy, resolutionNotes))
                .collect(Collectors.toList());
    }
    
    @Override
    public ColdChainAlertDTO markNotificationSent(Long id) {
        ColdChainAlert alert = coldChainAlertRepository.findById(id)
                .orElseThrow(() -> new RuntimeException("冷链报警不存在"));
        
        alert.setNotificationSent(true);
        ColdChainAlert updated = coldChainAlertRepository.save(alert);
        return convertToDTO(updated);
    }
    
    @Override
    public ColdChainAlertDTO createTemperatureAlert(Long coldChainRecordId, String alertType, 
                                                   String currentValue, String thresholdValue, 
                                                   String location, String gpsCoordinates) {
        ColdChainAlertDTO alertDTO = new ColdChainAlertDTO();
        alertDTO.setColdChainRecordId(coldChainRecordId);
        alertDTO.setAlertTime(LocalDateTime.now().format(FORMATTER));
        alertDTO.setAlertType(alertType);
        alertDTO.setSeverity(determineSeverity(alertType));
        alertDTO.setCurrentValue(currentValue);
        alertDTO.setThresholdValue(thresholdValue);
        alertDTO.setLocation(location);
        alertDTO.setGpsCoordinates(gpsCoordinates);
        alertDTO.setDescription(generateTemperatureAlertDescription(alertType, currentValue, thresholdValue));
        
        return createColdChainAlert(alertDTO);
    }
    
    @Override
    public ColdChainAlertDTO createHumidityAlert(Long coldChainRecordId, String alertType,
                                               String currentValue, String thresholdValue,
                                               String location, String gpsCoordinates) {
        ColdChainAlertDTO alertDTO = new ColdChainAlertDTO();
        alertDTO.setColdChainRecordId(coldChainRecordId);
        alertDTO.setAlertTime(LocalDateTime.now().format(FORMATTER));
        alertDTO.setAlertType(alertType);
        alertDTO.setSeverity(determineSeverity(alertType));
        alertDTO.setCurrentValue(currentValue);
        alertDTO.setThresholdValue(thresholdValue);
        alertDTO.setLocation(location);
        alertDTO.setGpsCoordinates(gpsCoordinates);
        alertDTO.setDescription(generateHumidityAlertDescription(alertType, currentValue, thresholdValue));
        
        return createColdChainAlert(alertDTO);
    }
    
    @Override
    public ColdChainAlertDTO createDeviceAlert(Long coldChainRecordId, String alertType,
                                             String description, String location, String gpsCoordinates) {
        ColdChainAlertDTO alertDTO = new ColdChainAlertDTO();
        alertDTO.setColdChainRecordId(coldChainRecordId);
        alertDTO.setAlertTime(LocalDateTime.now().format(FORMATTER));
        alertDTO.setAlertType(alertType);
        alertDTO.setSeverity(determineSeverity(alertType));
        alertDTO.setDescription(description);
        alertDTO.setLocation(location);
        alertDTO.setGpsCoordinates(gpsCoordinates);
        
        return createColdChainAlert(alertDTO);
    }
    
    @Override
    @Transactional(readOnly = true)
    public Map<String, Object> getAlertStatistics() {
        Map<String, Long> typeStats = getAlertCountByType();
        Map<String, Long> severityStats = getAlertCountBySeverity();
        
        Map<String, Object> statistics = new HashMap<>();
        statistics.put("byType", typeStats);
        statistics.put("bySeverity", severityStats);
        
        return statistics;
    }
    
    @Override
    @Transactional(readOnly = true)
    public Map<String, Long> getAlertCountByType() {
        List<Object[]> typeCounts = coldChainAlertRepository.countByAlertType();
        Map<String, Long> result = new HashMap<>();
        
        for (Object[] row : typeCounts) {
            result.put(row[0].toString(), (Long) row[1]);
        }
        
        return result;
    }
    
    @Override
    @Transactional(readOnly = true)
    public Map<String, Long> getAlertCountBySeverity() {
        List<Object[]> severityCounts = coldChainAlertRepository.countBySeverity();
        Map<String, Long> result = new HashMap<>();
        
        for (Object[] row : severityCounts) {
            result.put(row[0].toString(), (Long) row[1]);
        }
        
        return result;
    }
    
    @Override
    @Transactional(readOnly = true)
    public Long countUnresolvedAlerts(Long coldChainRecordId) {
        return coldChainAlertRepository.countUnresolvedAlerts(coldChainRecordId);
    }
    
    @Override
    public void sendAlertNotification(Long alertId) {
        // 这里可以集成短信、邮件或推送通知服务
        markNotificationSent(alertId);
    }
    
    @Override
    public void sendBatchNotifications(List<Long> alertIds) {
        alertIds.forEach(this::sendAlertNotification);
    }
    
    @Override
    public void cleanupOldResolvedAlerts(LocalDateTime cutoffTime) {
        coldChainAlertRepository.deleteOldResolvedAlerts(cutoffTime);
    }
    
    @Override
    public void deleteColdChainAlert(Long id) {
        coldChainAlertRepository.deleteById(id);
    }
    
    private void copyDtoToEntity(ColdChainAlertDTO dto, ColdChainAlert entity) {
        ColdChainRecord record = coldChainRecordRepository.findById(dto.getColdChainRecordId())
                .orElseThrow(() -> new RuntimeException("冷链记录不存在"));
        entity.setColdChainRecord(record);
        
        entity.setDescription(dto.getDescription());
        entity.setCurrentValue(dto.getCurrentValue());
        entity.setThresholdValue(dto.getThresholdValue());
        entity.setLocation(dto.getLocation());
        entity.setGpsCoordinates(dto.getGpsCoordinates());
        entity.setIsResolved(dto.getIsResolved() != null ? dto.getIsResolved() : false);
        entity.setResolvedBy(dto.getResolvedBy());
        entity.setResolutionNotes(dto.getResolutionNotes());
        entity.setNotificationSent(dto.getNotificationSent() != null ? dto.getNotificationSent() : false);
        
        if (dto.getAlertType() != null && !dto.getAlertType().isEmpty()) {
            entity.setAlertType(ColdChainAlert.AlertType.valueOf(dto.getAlertType().toUpperCase()));
        }
        
        if (dto.getSeverity() != null && !dto.getSeverity().isEmpty()) {
            entity.setSeverity(ColdChainAlert.Severity.valueOf(dto.getSeverity().toUpperCase()));
        }
        
        if (dto.getAlertTime() != null && !dto.getAlertTime().isEmpty()) {
            entity.setAlertTime(LocalDateTime.parse(dto.getAlertTime(), FORMATTER));
        } else {
            entity.setAlertTime(LocalDateTime.now());
        }
        
        if (dto.getResolvedTime() != null && !dto.getResolvedTime().isEmpty()) {
            entity.setResolvedTime(LocalDateTime.parse(dto.getResolvedTime(), FORMATTER));
        }
    }
    
    private ColdChainAlertDTO convertToDTO(ColdChainAlert entity) {
        ColdChainAlertDTO dto = new ColdChainAlertDTO();
        BeanUtils.copyProperties(entity, dto);
        
        dto.setColdChainRecordId(entity.getColdChainRecord().getId());
        dto.setAlertType(entity.getAlertType().name());
        dto.setSeverity(entity.getSeverity().name());
        dto.setAlertTime(entity.getAlertTime() != null ? entity.getAlertTime().format(FORMATTER) : null);
        dto.setResolvedTime(entity.getResolvedTime() != null ? entity.getResolvedTime().format(FORMATTER) : null);
        dto.setCreatedAt(entity.getCreatedAt() != null ? entity.getCreatedAt().format(FORMATTER) : null);
        
        return dto;
    }
    
    private String determineSeverity(String alertType) {
        switch (alertType.toUpperCase()) {
            case "TEMPERATURE_HIGH":
            case "TEMPERATURE_LOW":
                return "HIGH";
            case "DEVICE_OFFLINE":
            case "GPS_LOST":
                return "CRITICAL";
            case "BATTERY_LOW":
                return "MEDIUM";
            case "HUMIDITY_HIGH":
            case "HUMIDITY_LOW":
                return "MEDIUM";
            case "DOOR_OPENED":
            case "VIBRATION_HIGH":
                return "HIGH";
            default:
                return "LOW";
        }
    }
    
    private String generateTemperatureAlertDescription(String alertType, String currentValue, String thresholdValue) {
        switch (alertType.toUpperCase()) {
            case "TEMPERATURE_HIGH":
                return String.format("温度过高：当前温度 %s°C，超过阈值 %s°C", currentValue, thresholdValue);
            case "TEMPERATURE_LOW":
                return String.format("温度过低：当前温度 %s°C，低于阈值 %s°C", currentValue, thresholdValue);
            default:
                return "温度异常";
        }
    }
    
    private String generateHumidityAlertDescription(String alertType, String currentValue, String thresholdValue) {
        switch (alertType.toUpperCase()) {
            case "HUMIDITY_HIGH":
                return String.format("湿度过高：当前湿度 %s%%，超过阈值 %s%%", currentValue, thresholdValue);
            case "HUMIDITY_LOW":
                return String.format("湿度过低：当前湿度 %s%%，低于阈值 %s%%", currentValue, thresholdValue);
            default:
                return "湿度异常";
        }
    }
}